package Thread;


import org.junit.Test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 锁的实现原理基本是为了达到一个目的：让所有的线程都能看见某种标记。
 *     Synchronized是在对象头中设置标记实现这一目的，是一种JVM原生锁的实现.
 *     ReentrantLock和其他所有的基于lock接口实现的类，都是通过一个volitile修饰的int型变量，并保证每个线程都能拥有对该int的可见性和原子修改。其本质是基于AQS框架实现的。

 *  AQS（AbstractQueueSynchronizer类）是一个用来构建锁和同步器的框架，各种Lock包中的锁，甚至早期的FutureTask，都是基于AQS构建的。

 *  ReentrantLock是Lock的实现类，是一个互斥的同步锁。

 * 从功能角度讲：ReentrantLock比synchronized更精细，可以实现synchronized实现不了的功能：

 *  等待可中断：当长期持有锁的线程不释放锁时，正在等待的线程可以选择放弃等待，对处理执行时间非常长的同步块很有用。
 * 带超时的获取锁尝试：在指定时间范围内获取锁，如果到了时间仍无法获取就返回。
 * 可以判断是否有线程在等待获取锁。
 * 可以响应中断请求：与synchronized不同，当获取到锁的线程被中断时，能够响应中断，中断异常会被抛出，同时锁会被释放。
 * 可以实现公平锁
 * 从释放锁的角度讲：synchronized在JVM层面实现的，不但可以通过监控工具监测synchronized的锁定，代码异常时会自动释放锁；Lock加锁后必须手动释放锁。

 * 从性能角度讲：java6改进synchronized后，在竞争不激烈的情况下，synchronized性能高于ReentrantLock;高竞争情况下，synchronized性能会下降几十倍，
 * ReentrantLock性能会维持。
 *
 * condition:condition维护了一个线程等待队列，也是多线程间协调通讯的工具类，使得某个线程一起等待某个条件，满足条件才被唤醒，唤醒后，还需要重新竞争锁。

 * 问题：  tryLock() 和Lock()的区别是什么？

 *   tryLock()不会阻塞
 *   lock() 阻塞加锁

 * //等待锁的时间内可以做一些其他事情
 * while(!tryLock()){
 * //其他逻辑
 * ========等价========
 * lock()
 * }
 */
public class ReentrantLock_Study {

    private static Lock reentrantLock = new ReentrantLock(true);
    private static  Condition  condition= reentrantLock.newCondition()  ;


    volatile static int sum = 0;

    public static void m() {
        reentrantLock.lock();  // block until condition holds
        try {
            for (int j = 0; j < 100; j++) {
                sum++;

            }
            System.out.println(Thread.currentThread().getName() + " 结束:" + sum);
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                m();
            }, "线程:"+ i).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(sum);
    }


    /**
     * 多个线程之间顺序调用，实现：A->B->C 三个线程启动，要求如下：
     *    AA线程打印5次
     *    BB线程打印10次
     *    cc线程打印15次
     *
     *
     *  来10轮
     */
    @Test
    public void SyncAndReentrantLockDemo(){

        printClass  pc=new printClass();
        new Thread(()->{
            for (int i = 1; i < 11; i++) {
                System.out.println("第"+" "+i+"论");
                pc.printA();
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                pc.printB();
            }
        },"BB").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                pc.printC();
            }
        },"CC").start();


    }

    /**
     * 无锁生产消费
     */

    @Test
    public void produceAndConsumer() throws InterruptedException {

        ProduceAndConsumer  pc=new ProduceAndConsumer(new ArrayBlockingQueue<String>(10),new AtomicInteger());

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"\t生成线程启动");
                pc.Produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"AA").start();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"\t消费线程启动");
                pc.Consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"BB").start();


      //执行5S 后主线程停止
        TimeUnit.SECONDS.sleep(5);
        pc.stop();


    }



    /**
     * 使用无锁实现生产消费模式
     */
    class ProduceAndConsumer{
        private volatile boolean FLAG=true;
        private BlockingQueue<String>  blockingQueue;
        private AtomicInteger  atomicInteger;
        public  ProduceAndConsumer(BlockingQueue<String>  blockingQueue,AtomicInteger atomicInteger){
            this.blockingQueue=blockingQueue;
            this.atomicInteger=atomicInteger;
        }
        //生产者生产
        public void Produce() throws InterruptedException {
            String data=null;
            while (FLAG){
                data = atomicInteger.incrementAndGet() + "";
                boolean offer = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
                if(offer){
                    System.out.println(Thread.currentThread().getName()+"生产成功"+data);
                }else{
                    System.out.println(Thread.currentThread().getName()+"生产失败");

                }
            }
            System.out.println("大老板叫停了");
        }

        //消费者消费
        public void Consume() throws InterruptedException {
            String result=null;
            while (FLAG){
                 result = blockingQueue.poll(2L,TimeUnit.SECONDS);
                 if(result==null||result.equals("")){
                     FLAG=false;
                     System.out.println(Thread.currentThread().getName()+"超过2S没有等到消息"+result);
                     return;

                 }
                System.out.println(Thread.currentThread().getName()+"消费成功："+result);


            }
        }

        public void stop(){
            FLAG=false;
        }
    }

    /**
     *  Lock  unlock实现唤醒阻塞机制
     */
    class   printClass{

        private volatile int number=1;
        Lock  locks = new ReentrantLock();

        Condition conditionA = locks.newCondition();
        Condition conditionB = locks.newCondition();
        Condition conditionC = locks.newCondition();

        /**
         * A线程打印5次 然后通知B线程
         */
        public void printA(){
            try{
                locks.lock();

                //判断
                while (number!=1){
                    conditionA.await();
                 //   Thread.sleep(1);
                }

                //干活
                printMethod(1);

                //通知
               number=2;
               conditionB.signal();

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                locks.unlock();
            }

        }

        /**
         * B线程打印10次   通知C线程
         */
        public void printB(){
            try{
                locks.lock();

                //判断
                while (number!=2){
                    conditionB.await();
                  //  Thread.sleep(1);
                }

                //干活
                printMethod(2);

                //通知
                number=3;
                conditionC.signal();

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                locks.unlock();
            }

        }


        /**
         * C线程打印10次   通知A线程
         */
        public void printC(){
            try{
                locks.lock();

                //判断
                while (number!=3){
                    conditionC.await();
                  //  Thread.sleep(1);
                }

                //干活
                printMethod(3);

                //通知
                number=1;
                conditionA.signal();

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                locks.unlock();
            }

        }

        private void printMethod(int number) {
            for (int i = 1; i <= number; i++) {
                System.out.println(Thread.currentThread().getName()+"打印："+i+" 次");

            }
        }
    }
}
